package jmine.tec.script.persistent.services.alterar;

import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.API_PREFIX;
import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.DESCRICAO;
import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.FINALIDADE;
import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.IDENTIFICADOR;
import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.INJECT_REGEX;
import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.INJECT_TEMPLATE;
import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.LINGUAGEM;
import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.MNEMONICO;
import static jmine.tec.script.persistent.services.incluir.AbstractScriptService.TEXTO;

import java.util.Map;
import java.util.Set;

import jmine.tec.di.annotation.Injected;
import jmine.tec.script.persistent.domain.Script;
import jmine.tec.script.persistent.domain.ScriptDependency;
import jmine.tec.script.persistent.domain.ScriptLanguage;
import jmine.tec.script.persistent.domain.ScriptPurpose;
import jmine.tec.services.annotations.Execution;
import jmine.tec.services.annotations.Input;
import jmine.tec.services.annotations.Output;
import jmine.tec.services.annotations.WithPreffix;
import jmine.tec.services.annotations.WithRegex;
import jmine.tec.services.api.ServiceExecutionException;
import jmine.tec.services.api.ServiceProperties;
import bancosys.tec.persist.dao.BeanNotFoundException;

/**
 * Classe abstrata de serviços de alteração de script
 * 
 * @author lundberg
 * @param <S> tipo de script alterado
 */
public abstract class AbstractScriptService<S extends Script> {

    // Injected
    private ServiceProperties serviceProperties;

    // Obrigatório
    private S script;

    private ScriptLanguage linguagem;

    private ScriptPurpose finalidade;

    private Set<Script> apis;

    private Map<String, String> dependencias;

    /**
     * Altera um script Hydra já existente ao sistema.
     * 
     * @throws BeanNotFoundException caso não encontre o script com o dado mnemonico
     * @throws ServiceExecutionException ServiceExecutionException
     */
    @Execution
    @Output(propertyName = IDENTIFICADOR)
    public void execute() throws BeanNotFoundException, ServiceExecutionException {
        this.script.getApis().clear();
        this.script.getApis().addAll(this.apis);
        this.script.setLanguage(this.linguagem);
        this.script.setDescricao(this.serviceProperties.getRaw(DESCRICAO));
        this.script.setPurpose(this.finalidade);
        this.script.setBody(this.serviceProperties.getRaw(TEXTO));
        this.script.getDependencies().clear();
        for (String key : this.dependencias.keySet()) {
            ScriptDependency dependency = new ScriptDependency();
            dependency.setBean(this.serviceProperties.getRaw(String.format(INJECT_TEMPLATE, key)));
            dependency.setVariable(key);
            this.script.getDependencies().add(dependency);
        }
        this.fillSpecific(this.script);
        this.script.getPersister().save();
    }

    /**
     * Preenche dados específicos da implementação.
     * 
     * @param scr script
     * @throws ServiceExecutionException ServiceExecutionException
     */
    protected abstract void fillSpecific(S scr) throws ServiceExecutionException;

    /**
     * @param serviceProperties the serviceProperties to set
     */
    @Injected
    public void setServiceProperties(ServiceProperties serviceProperties) {
        this.serviceProperties = serviceProperties;
    }

    /**
     * @param texto the texto to set
     */
    @Input(fieldName = TEXTO)
    public void setTexto(String texto) {
        // OK
    }

    /**
     * @param script the script to set
     */
    @Input(fieldName = MNEMONICO)
    public void setScript(S script) {
        this.script = script;
    }

    /**
     * @param linguagem the linguagem to set
     */
    @Input(fieldName = LINGUAGEM)
    public void setLinguagem(ScriptLanguage linguagem) {
        this.linguagem = linguagem;
    }

    /**
     * @param finalidade the finalidade to set
     */
    @Input(fieldName = FINALIDADE, required = false)
    public void setFinalidade(ScriptPurpose finalidade) {
        this.finalidade = finalidade;
    }

    /**
     * @param apis the apis to set
     */
    @WithPreffix(preffix = API_PREFIX)
    public void setApis(Set<Script> apis) {
        this.apis = apis;
    }

    /**
     * @param dependencias the dependencias to set
     */
    @WithRegex(regex = INJECT_REGEX, groupIndex = 1)
    public void setDependencias(Map<String, String> dependencias) {
        this.dependencias = dependencias;
    }

    /**
     * @param descricao the descricao to set
     */
    @Input(fieldName = DESCRICAO, required = false)
    public void setDescricao(String descricao) {
        // Descricao eh coletada RAW direto do serviceProperties
        // Esse metodo esta aqui apenas para documentacao do parametro
    }
}
